package com.yc.anno;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.yc.fav.entity.Fav;
import com.yc.fav.mapper.FavMapper;
import org.apache.ibatis.annotations.*;

import java.lang.reflect.*;
import java.sql.ResultSet;
import java.util.*;

public class SqlSession {

    public <T> T getMapper(Class<T> cls) {
        // 反射 + 动态代理
        // cls.newInstance(); // new A();

        InvocationHandler a;
        // 使用动态代理创建对象 (Spring IOC 也使用的是该技术)
        T mapper = (T) Proxy.newProxyInstance(cls.getClassLoader(),
                new Class[]{cls}, (proxy, method, args) -> {
                    System.out.println("method = " + method);
                    System.out.println("args = " + Arrays.toString(args));
                    Object ret = null;
                    if(method.isAnnotationPresent(Select.class)){
                        ret = invokeSelect(method,args);
                    } else if( method.isAnnotationPresent(Update.class)){
                        ret = invokeUpdate(method,args);
                    } else if( method.isAnnotationPresent(Insert.class)){
                        ret = invokeInsert(method,args);
                    } else if( method.isAnnotationPresent(Delete.class)){
                        ret = invokeDelete(method,args);
                    } else if( method.getName().equals("updateById") ){
                        ret = updateById(method,args);
                    }
                    return ret;
                }
        );

        return mapper;
    }

    public Object invokeSelect(Method method, Object[] args){
        // 获取语句
        Select select = method.getAnnotation(Select.class);
        String sql = select.value()[0];
        // 获取参数和收集参数
        Map<String,Object> paramMap = new HashMap<>();
        if(args == null || args.length == 0){
            // 无参数sql
        } else {
            // 有参数
            int index = 0;
            for (Parameter parameter : method.getParameters()) {
                if(parameter.isAnnotationPresent(Param.class)){
                    String name = parameter.getAnnotation(Param.class).value();
                    Object value  = args[index];
                    paramMap.put(name,value);
                } else {
                    // TODO 没有加 @Param 注解的参数
                }
                index ++;
            }
        }
        // 替换参数  select * from A where a = #{id} and b = #{sn} => ?
        List<Object> params = new ArrayList<>();
        while(sql.contains("#")){
            int indexBegin =  sql.indexOf("#");
            int indexEnd =  sql.indexOf("}",indexBegin+1);
            String name = sql.substring(indexBegin+2,indexEnd);
            Object value = paramMap.get(name);
            params.add(value);
            sql = sql.replaceFirst("#\\{.+?}","?");
        }
        System.out.println("sql = " + sql);
        System.out.println("params = " + params);

        // TODO 执行语句
        System.out.println("返回值类型 " + method.getReturnType());
        Class c = getGenericTypeByMethodReturn(method);
        System.out.println("泛型类型 = " + c);

        // 封装结果集
        // 伪代码
//        ResultSet rs = null;
//        List ret = new  ArrayList<>();
//        while(rs.next()){
//            Object object = c.newInstance(); // new Fav();
//            for (Field field : c.getDeclaredFields()) {
//                field.setAccessible(true);
//                String property = field.getName();
//                // 根据属性名从 rs 中获取字段值
//                Object value = rs.getObject(property);
//                // 通过反射设置到 object 对象中
//                field.set(object,value);
//            }
//            // 添加到返回值集合
//            ret.add(object);
//        }
        return null;
    }
    public Object invokeUpdate(Method method, Object[] args){
        // TODO
        return null;
    }
    public Object invokeDelete(Method method, Object[] args){
        // TODO
        return null;
    }
    public Object invokeInsert(Method method, Object[] args){
        // TODO
        return null;
    }

    public Object updateById(Method method, Object[] args){
        // 构建语句
        // 表名
        String tableName = args[0].getClass().getSimpleName();
        // 主键字段名
        String idName = null;
        Object idValue = null;
        // 统计更新字段
        Map<String,Object> paramMap = new LinkedHashMap<>();
        try{
            for (Field field : args[0].getClass().getDeclaredFields()) {
                field.setAccessible(true);
                // TODO 查如何判断静态属性
                if(field.getName().equals("serialVersionUID")){
                    continue;
                }
                if (field.isAnnotationPresent(TableId.class)) {
                    idName = field.getName();
                    idValue = field.get(args[0]);  // idValue = fav.getId()
                } else {
                    String columnName = field.getName();
                    Object columnValue = field.get(args[0]);
                    if(columnValue != null){
                        paramMap.put(columnName,columnValue);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String sql = "update %s set %s where %s=? ";
        String sets = "";
        for (String column : paramMap.keySet()) {
            sets += column + "=?,";
        }
        sets = sets.substring(0, sets.length() - 1);
        sql = String.format(sql, tableName, sets, idName);
        List<Object> params = new ArrayList<>();
        params.addAll(paramMap.values());
        params.add(idValue);

        System.out.println("sql = " + sql);
        System.out.println("params = " + params);

        // TODO 使用 dbhelper 执行 update 语句, 返回 int 值
        // 之前报错原因: udpateById 方法返回值是 int 类型, 不能返回 null
        return 0;
    }

    public static Class getGenericTypeByMethodReturn(Method method) {
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) returnType;
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            String cls = actualTypeArguments[0].toString().substring("class ".length());
            try {
                return Class.forName(cls);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("构建类型对象失败: " + cls, e);
            }
        }
        throw new RuntimeException("未找到返回值的泛型类型: " + method);
    }

    public static void main(String[] args) {
        SqlSession sqlSession = new SqlSession();
        FavMapper favMapper = sqlSession.getMapper(FavMapper.class);
        favMapper.queryByTidAndUid(1,1);
        System.out.println("--------------------------------");
        favMapper.selectById(100);
        System.out.println("--------------------------------");
        Fav fav = new Fav();
        fav.setId(1);
        fav.setName("淘宝");
        fav.setAddr("taobao.com");
        // update fav set name=?,addr=? where id=?
        favMapper.updateById(fav);

    }

}
